﻿namespace Legend.Core.Tests.Collections
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using NUnit.Framework;
    using Legend.Collections;
    using FakeItEasy;
    using System.Runtime.Serialization.Formatters.Binary;
    using System.IO;

    [TestFixture]
    public class LazyLoadedListTests
    {
        [Test]
        public void Should_be_same_sequence_as_passed_in_enumerable()
        { 
            // Arrange
            var original = new[] { 1, 2, 3 };

            // Act
            var list = CreateList(original);

            // Assert
            Assert.That(list, Is.EquivalentTo(original));
        }

        [Test]
        public void Should_not_evaluate_passed_in_enumerable_more_than_once()
        { 
            // Arrange
            var first = true;

            var functions = new Func<string>[] 
            {
                () => 
                {
                    if (first) 
                    {
                        first = false;
                        return "Hello";
                    }

                    throw new Exception();
                }
            };

            var original = functions.Select(x => x.Invoke());

            // Act
            var list = CreateList(original).Select(x => x);

            // Assert
            list.Single();
            
            Assert.DoesNotThrow(() => list.Single());
        }

        [Test]
        public void Should_not_enumerate_passed_in_collection_before_used()
        {
            // Arrange
            var original = A.Fake<IEnumerable<object>>();

            // Act
            CreateList(original);

            // Assert
            A.CallTo(() => original.GetEnumerator()).MustNotHaveHappened();
        }

        [Test]
        public void Should_be_serializable()
        {
            Assert.That(CreateList(Enumerable.Range(1, 10).ToArray()), Is.BinarySerializable);
        }

        [Test]
        public void Should_get_index_of_item_correctly()
        { 
            // Arrange
            var list = CreateList(1, 2, 3);

            // Act
            var index = list.IndexOf(2);

            // Assert
            Assert.That(index, Is.EqualTo(1));
        }

        [Test]
        public void Should_insert_at_end()
        { 
            // Arrange
            var list = CreateList(1);

            // Act
            list.Insert(1, 2);

            // Assert
            Assert.That(list, Is.EquivalentTo(CreateList(1, 2)));
        }

        [Test]
        public void Should_remove_correctly()
        { 
            // Arrange
            var list = CreateList("foo", "bar");

            // Act
            list.RemoveAt(0);

            // Assert
            Assert.That(list, Is.EquivalentTo(CreateList("bar")));
        }

        [Test]
        public void Should_be_able_to_get_value_through_indexer()
        { 
            // Arrange
            var list = CreateList(0, 1);

            // Act
            // Assert
            Assert.That(list[1], Is.EqualTo(1));
        }

        [Test]
        public void Should_be_able_to_set_value_through_indexer()
        { 
            // Arrange
            var list = CreateList(0);

            // Act
            list[0] = 1;

            // Assert
            Assert.That(list, Is.EquivalentTo(CreateList(1)));
        }

        [Test]
        public void Should_be_able_to_add()
        { 
            // Arrange
            var list = CreateList<string>();

            // Act
            list.Add("foo");

            // Assert
            Assert.That(list, Is.EquivalentTo(CreateList("foo")));
        }

        [Test]
        public void Should_empty_list_when_clearing()
        { 
            // Arrange
            var list = CreateList("foo", "bar");

            // Act
            list.Clear();

            // Assert
            Assert.That(list, Is.Empty);
        }

        [Test]
        public void Should_get_that_list_does_not_contain_item_that_is_not_there()
        { 
            // Arrange
            var list = CreateList("some", "elements");

            // Act, Assert
            Assert.That(list.Contains("not there"), Is.False);
        }

        [Test]
        public void Should_get_that_list_contains_item_that_is_there()
        {
            // Arrange
            var list = CreateList("some", "elements");

            // Act, Assert
            Assert.That(list.Contains("some"), Is.True);
        }

        [Test]
        public void Should_copy_elements_correctly_to_array()
        {
            // Arrange
            var list = CreateList(1, 2, 3);
            var array = new int[3];
            
            // Act
            list.CopyTo(array, 0);

            // Assert
            Assert.That(array, Is.EquivalentTo(list));
        }

        [Test]
        public void Should_get_number_of_elements_from_count()
        { 
            // Arrange
            var list = CreateList(1, 2);

            // Act
            var count = list.Count;

            // Assert
            Assert.That(count, Is.EqualTo(2));
        }

        [Test]
        public void Should_not_be_read_only()
        { 
            // Arrange
            var list = CreateList<object>();

            // Act, Assert
            Assert.That(list.IsReadOnly, Is.False);
        }

        [Test]
        public void Should_remove_item()
        { 
            // Arrange
            var list = CreateList(1, 2, 3);

            // Act, Assert
            Assert.That(list.Remove(2), Is.True);
            Assert.That(list, Is.EquivalentTo(CreateList(1, 3)));
        }

        [Test]
        public void Should_fail_when_sequence_is_null()
        { 
            // Arrange

            // Act, Assert
            Assert.That(() =>
            {
                CreateList(default(int[]));
            }, Throws.InstanceOf<ArgumentNullException>());
        }
        
        private static IList<T> CreateList<T>(params T[] items)
        {
            return items.LazyToList();
        }
    }

}
